/*
 * Generic xmit layer for the PXA USB client function
 *
 * This code was loosely inspired by the original version which was
 * Copyright (c) Compaq Computer Corporation, 1998-1999
 * Copyright (c) 2001 by Nicolas Pitre
 *
 * (C) Copyright 2006 Marvell International Ltd.
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 02-May-2002
 *   Frank Becker (Intrinsyc) - derived from sa1100 usb_send.c
 *
 * TODO: Add support for DMA.
 *
 */

#include "types.h"
#include "arch.h"
#include "time.h"
#include "mhn-regs.h"
#include "xllp_u2d.h"
#include "mhn_u2d_ctl.h"
#include "util.h"
#include "asm/errno.h"

#define MHN_U2D_EP1_DBG 0
#if MHN_U2D_EP1_DBG
#define DBGOUT(x) x
#else
#define DBGOUT(x)     /* gcc will remove all the debug code for us */
#endif

#define DELAY2() do { \
        __asm__ volatile (\
        "mov ip, #0x1000\n"\
        "loop:  mov r0, r0\n"\
        "sub ip, ip, #1\n"\
        "cmp ip, #0\n"\
        "bne    loop\n");\
    }while(0);



#if 0
#define DELAY() do { \
        __asm__ volatile (\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        "mov r0, r0\n"\
        ); \
    } while(0)
#endif

extern XLLP_U2D_T   xllp_u2d;
extern int arp_t;
extern P_XLLP_DMAC_DESCRIPTOR_T ep1_dma_desc;

#ifndef MMU_ENABLE
static char  ep1_buf[1024];
#else
extern int *ep1_buf;
#endif
static int   ep1_len = 0;
static int   ep1_remain;
usb_callback_t ep1_callback = 0;
//static int tx_pktsize;

static P_XLLP_U2D_EP_CONFIG ep1_config = &xllp_u2d.ConfigTable[1];
static P_XLLP_U2D_TRANSFER ep1_txfr = &xllp_u2d.u2dEpXferTable[1];
static void ep1_done(int flag);
//static int u2d_info();

void dump_buffer(char *buf, unsigned length)
{
    int i;

    DBGOUT(printf("%s, buffer:%p, total length:%d\n", __func__, buf, length));
    for (i = 0; i < length; i++) {
        if (0 == i % 10)
            DBGOUT(printf("\n"));
        DBGOUT(printf (" %2x", c[i]));
    }
    DBGOUT(printf("\n"));
}

extern int req_pending;
void dump_info(int ep_num)
{
    //P_XLLP_U2D_TRANSFER txfr = &xllp_u2d.u2dEpXferTable[ep_num];
    P_XLLP_U2D_EP_CONFIG ep_config = &xllp_u2d.ConfigTable[ep_num];
    XLLP_DMAC_DESCRIPTOR_T *desc;
    __u32 csr, cr, en;
    int i;

    DBGOUT(printf("ep%d info:\n", ep_num));

    if(ep_num==1) {
        csr = U2DCSRA;
        cr = U2DEPCRA;
        en = U2DENA;
    }
    else {
        csr = U2DCSRB;
        cr = U2DEPCRB;
        en = U2DENB;
    }

    DBGOUT(printf("\tu2dcsr %02x u2dcr:0x%x u2denr:0x%x dcsr %x, dcmd %x\n",
                csr, cr, en, U2DMACSR(ep_num), U2DMACMDR(ep_num)));


    DBGOUT(printf("\tET%2d ED%2d EN%2d CN%2d IN%2d AISN%2d MPS %2d HBW%2d\n"    \
            "\tbuffersize:%d vDmaDescriptor:%p pDmaDescriptor:%x nDescriptors:%d\n",
            ep_config->ET, ep_config->ED, (int)ep_config->EN,
            (int)ep_config->CN, (int)ep_config->InterfaceNumber, (int)ep_config->AISN,
            (int)ep_config->MPS, (int)ep_config->HBW, (int)ep_config->bufferSize,
            ep_config->vDmaDescriptor, (unsigned)ep_config->pDmaDescriptor,
            (int)ep_config->nDescriptors));

    if(!ep_config->ED)
        DBGOUT(printf("\treq_pending = %d", req_pending));

    desc = ep_config->vDmaDescriptor;
        for(i=0;i<3;i++) {
            DBGOUT(printf("\tdadr:%x, dsdr:%x, dtdr:%x, dcmd:%x \n",
                (unsigned)desc->ddadr, (unsigned)desc->dsadr,
                (unsigned)desc->dtadr, (unsigned)desc->dcmd));
            desc++;
        }
}

/* device state is changing, async */
void ep1_state_change_notify( int new_state )
{
}

/* set feature stall executing, async */
void ep1_stall( void )
{
}

extern int ep2_begin(int);
static void ep1_send_packet(void)
{
    DBGOUT(printf("^^^^^^In ep1_send_packet!U2DCSRA = %x, U2DCSRB %x\n", U2DCSRA, U2DCSRB));
    int count=100, complete = 0;
    __u32 dcsr, status;

    U2DCSRA |= U2DCSR_FEF;
    DBGOUT(printf("after flush fifo A, U2DCSRA = %x\n", U2DCSRA));
    U2DCSRA |= U2DCSR_DME;
    DBGOUT(printf("after enable DMA for A, U2DCSRA = %x\n", U2DCSRA));
    U2DMACSR(1) &= ~U2DMACSR_RUN;
    DBGOUT(printf("u2dcr = %x, csrA = %x, dmacsrA = %x\n", U2DCR, U2DCSRA, U2DMACSR(1)));

    /* init the params for transfer */
    ep1_txfr->direct = 1;
    ep1_txfr->EnableEndIrq = 1;

    ep1_txfr->physicalAddress = (XLLP_UINT32_T)ep1_buf;
    ep1_txfr->isZeroPackage = (ep1_len==0)?1:0;
    DBGOUT(printf("ep1_len = %d, ep1_config->MPS = %d\n", ep1_len, ep1_config->MPS));
    ep1_txfr->len = ep1_len;
    ep1_txfr->udcEndpointNum = 1;

    ep1_config->vDmaDescriptor = ep1_dma_desc;
    ep1_config->pDmaDescriptor = ep1_dma_desc;
    DBGOUT(printf("dmadescriptor virtual address: %x, physical address: %x\n", ep1_config->vDmaDescriptor, ep1_config->pDmaDescriptor));
    ep1_config->nDescriptors = DMA_DESC_NUM/2;

    XllpU2DStartTransfer(&xllp_u2d, ep1_txfr);
    DBGOUT(printf("before run!dmacsrA = %x, csra = %x, dmacsrB %x, csrB %x\n", U2DMACSR(1), U2DCSRA, U2DMACSR(2), U2DCSRB));
    dcsr = U2DMACSR(1);
    DBGOUT(printf("before set dmacsrA, dcsr =%x\n", dcsr));
    dcsr &= U2DMACSR_MASK;
    dcsr |= U2DMACSR_RUN;
    U2DMACSR(1) = dcsr;
    DBGOUT(printf("after run!dmacsr = %x, csra = %x\n", U2DMACSR(1), U2DCSRA));
    while(count--);
    DBGOUT(printf("start to transfer\n"));
    do {
        dcsr = U2DMACSR(1);
        status = U2DCSRA;
        DBGOUT(printf("dmacsr = %x, csra = %x, dmaint = %x\n", dcsr, status, U2DMAINT));
        DBGOUT(printf("U2DMADADRA = %x\t", U2DMADADR(1)));
        DBGOUT(printf("U2DMASADRA = %x\t", U2DMASADR(1)));
        DBGOUT(printf("U2DMATADRA = %x\t", U2DMATADR(1)));
        DBGOUT(printf("U2DMACMDRA = %x\n", U2DMACMDR(1)));

        if ((dcsr & U2DMACSR_ENDINTR) && (status & U2DCSR_PC)) {
            DELAY2();

            DBGOUT(printf("%s: ep1 IN DMA end\n", __FUNCTION__));
            ep1_remain = 0;
            DBGOUT(printf("U2DMACSR %x\n", U2DMACSR(1)));

            DBGOUT(printf("U2DMACSR %x\n", U2DMACSR(1)));

            U2DMACSR(1) &= ~U2DMACSR_RUN;

            DBGOUT(printf("%s ep1 snd done, csr %x dcsr %x dadr %x dcmd %x\n",
                __FUNCTION__, U2DCSRA, U2DMACSR(1), U2DMADADR(1), U2DMACMDR(1)));
            ep1_done(0);
            DBGOUT(printf("U2DMACSR %x\n", U2DMACSR(1)));
            complete++;
        }
    }while(!complete );

    DBGOUT(printf("%s: exit, ep1_remain=%d bytes, csrA %x dmacsrA %x, dmaint %x, dmacsrB %x, csrB %x\n",
            __FUNCTION__, ep1_remain, U2DCSRA, U2DMACSR(1), U2DMAINT, U2DMACSR(2), U2DCSRB));
}

static void ep1_start(void)
{
    DBGOUT(printf("^^^^^^^In ep1_start!\n"));
    XllpU2DEnableInterrupt(&xllp_u2d, 1,    U2D_SHORT_PACKET_COMPL_INT|U2D_DMA_STOP);
    XllpU2DDisableInterrupt(&xllp_u2d, 1, U2D_PACKET_COMPL_INT);
    DBGOUT(printf("start to send packet\n"));
    ep1_send_packet();
}

static void ep1_done(int flag)
{
    if (ep1_len) {
        ep1_len = 0;
        if ((int)ep1_callback!=0xffffffff) {
            DBGOUT(printf("ep1_callback %x\n",ep1_callback));
        }
    }

    XllpU2DDisableInterrupt(&xllp_u2d, 1,
        U2D_SHORT_PACKET_COMPL_INT|U2D_PACKET_COMPL_INT|U2D_FIFO_ERROR_INT|U2D_DMA_STOP);
}

int ep1_init(int chn)
{
    ep1_len = 0;

    ep1_txfr->direct = 1;
    ep1_txfr->EnableEndIrq = 1;
    ep1_txfr->udcEndpointNum = 1;

    ep1_config->vDmaDescriptor = ep1_dma_desc;
    ep1_config->pDmaDescriptor = ep1_dma_desc;
    ep1_config->nDescriptors = DMA_DESC_NUM/2;

    return 0;
}

void ep1_reset(void)
{
    U2DCSRA = U2DCSR_PC|U2DCSR_FEF;
    ep1_done(-EINTR);
    TRACEOUT
}

void ep1_int_hndlr(int flag)
{
    //int status = U2DCSRA;
    int dcsr = U2DMACSR(1);

    return;
    /* dma */
    if (dcsr & U2DMACSR_BUSERRINTR) {
        DBGOUT(printf("%s: bus error, u2dmacsr1:0x%x\n", __FUNCTION__, dcsr));
        DBGOUT(printf("dcsr:%x, ddadr:%x, dsadr:%x, dtadr:%x, dcmd:%x\n",
            U2DMACSR(1), U2DMADADR(1), U2DMASADR(1),
            U2DMATADR(1), U2DMACMDR(1)));
        U2DMACSR(1) = U2DMACSR_BUSERRINTR;
    }

    if ((dcsr & U2DMACSR_ENDINTR) || (dcsr & U2DMACSR_STOPINTR)) {
        U2DMACSR(1) = U2DMACSR_ENDINTR | U2DMACSR_STOPINTR;

        DBGOUT(printf("%s: ep1 IN DMA end\n", __FUNCTION__));
        ep1_remain = 0;
        XllpU2DDisableInterrupt(&xllp_u2d, 1, U2D_DMA_STOP);
        ep1_done(0);
    }

    if (ep1_remain != 0) {
        ep1_start();
    }
}

int ep1_send(char *buf, int len, usb_callback_t callback)
{
    DBGOUT(printf("^^^^^^In ep1_send!U2DCSRA= %x, U2DCSRB %x\n", U2DCSRA, U2DCSRB));

    DBGOUT(printf( "pxa_usb_send: data len=%d state=%d ep1_len=%d\n", len, usbd_info.state, ep1_len));

    memcpy(ep1_buf, buf, len);
    ep1_len = len;
    ep1_callback = callback;
    ep1_remain = len;
    ep1_start();

    return len;
}

int ep1_xmitter_avail( void )
{
    if (usbd_info.state != USB_STATE_CONFIGURED)
        return -ENODEV;
    if (ep1_len)
        return -EBUSY;
    return 0;
}

